<template>
    <!-- 扫描弹窗 -->
    <el-dialog class="scan-pdf-dialog" v-model="showPdfPreview" :show-close="false" fullscreen @open="onOpen" destroy-on-close>
        <!-- 标题 + 选项 -->
        <template #header="{ close }">
            <div class="scan-pdf-dialog-options">
                <div>
                    <el-pagination
                        v-model:current-page="currentPage"
                        layout="prev, pager, next"
                        :page-count="numPages"
                        @current-change="handleCurrentChange"
                    />
                </div>
                <el-button type="danger" @click="close" :icon="CircleCloseFilled">关闭</el-button>
            </div>
        </template>
        <!-- 内容 -->
        <div class="scan-pdf-dialog-content">
            <el-scrollbar height="calc(100vh - 50px)">
                <canvas id="pdf-container"></canvas>
            </el-scrollbar>
        </div>
    </el-dialog>
</template>

<script setup lang="ts">
import { CircleCloseFilled } from '@element-plus/icons-vue';
import * as pdfjs from 'pdfjs-dist';
import { type PDFDocumentProxy } from 'pdfjs-dist';

// 组件挂载完毕时，加载pdfjs工作线程
onMounted(() => {
    // 加载pdfjs worker
    pdfjs.GlobalWorkerOptions.workerSrc = import.meta.env.VITE_APP_PDF_WORKER;
});

/* ******************************通用属性/方法****************************** */
const showPdfPreview = defineModel('showPdfPreview', { required: true, type: Boolean, default: false });
const props = defineProps<{
    pdfData: ArrayBuffer | undefined;
}>();

// pdf
let pdfDoc: null | PDFDocumentProxy = null;
let pageRendering = false;
// 缩放
const scale = ref(1.5);
// 当前页码
const currentPage = ref(1);
// 总页码
const numPages = ref(0);

/* ******************************核心方法****************************** */
// 当对话框打开时扫描父组件传来的文件
const onOpen = async () => {
    // 读取文件
    const loadingTask = pdfjs.getDocument(props.pdfData);
    loadingTask.promise.then((pdfDoc_) => {
        // 获取pdf
        pdfDoc = pdfDoc_;
        // 获取总页码
        numPages.value = pdfDoc.numPages;
        // 渲染页面
        renderPage(currentPage.value);
    });
};

// 渲染页面
const renderPage = (num: number) => {
    // 渲染中
    pageRendering = true;
    // 获取pdf
    if (pdfDoc === null) {
        ElMessage.error('加载pdf解析器失败');
        return;
    }
    // 渲染pdf页面
    pdfDoc.getPage(num).then(function (page) {
        // 获取canvas元素
        const canvas = document.getElementById('pdf-container') as HTMLCanvasElement;
        const context = canvas.getContext('2d');
        // 外部缩放
        const outputScale = window.devicePixelRatio || 1;
        const transform = outputScale !== 1 ? [outputScale, 0, 0, outputScale, 0, 0] : null;
        // 获取页面尺寸
        const viewport = page.getViewport({ scale: scale.value });
        canvas.width = Math.floor(viewport.width * outputScale);
        canvas.height = Math.floor(viewport.height * outputScale);
        canvas.style.width = Math.floor(viewport.width) + 'px';
        canvas.style.height = Math.floor(viewport.height) + 'px';

        // 渲染上下文
        const renderContext = {
            canvasContext: context,
            transform: transform,
            viewport: viewport,
        };

        // 渲染页面
        const renderTask = page.render(renderContext as any);

        // 等待渲染完成
        renderTask.promise.then(function () {
            // 渲染完成
            pageRendering = false;
        });
    });
};

// 页码改变时, 渲染新的页面
const handleCurrentChange = (val: number) => {
    currentPage.value = val;
    renderPage(currentPage.value);
};
</script>

<style scoped lang="scss">
/* 扫描弹窗样式 */
.scan-pdf-dialog {
    overflow: hidden !important;
    /* 扫描弹窗标题 + 选项样式 */
    .scan-pdf-dialog-options {
        height: 40px !important;
        display: flex;
        align-items: center;
        justify-content: space-between;
    }

    /* 扫描弹窗内容样式 */
    .scan-pdf-dialog-content {
        height: calc(100vh - 90px);
        width: 100%;
        display: flex;
        flex-wrap: wrap;
        justify-content: center;
    }
}
</style>
